{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "\n",
    "# 读取原始数据\n",
    "df = pd.read_csv('d:/code/py_machine/StudentsPerformance_with_edu_score.csv')\n",
    "\n",
    "# 1. 删除parental level of education列\n",
    "df = df.drop(columns=['parental level of education'])\n",
    "\n",
    "# 2. 处理lunch列 (转为整数)\n",
    "df['lunch'] = df['lunch'].map({'standard': 1, 'free/reduced': 0}).astype(int)\n",
    "\n",
    "# 3. 处理test preparation course列 (转为整数)\n",
    "df['test preparation course'] = df['test preparation course'].map({'completed': 1, 'none': 0}).astype(int)\n",
    "\n",
    "# 4. 计算各科平均分数并减去平均值，结果保留两位小数\n",
    "for col in ['math score', 'reading score', 'writing score']:\n",
    "    mean_score = df[col].mean()\n",
    "    df[col] = (df[col] - mean_score).round(2)  # 保留两位小数\n",
    "\n",
    "# 保存处理后的数据\n",
    "df.to_csv('d:/code/py_machine/sp_data.csv', index=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "import seaborn as sns\n",
    "\n",
    "# 读取数据\n",
    "df = pd.read_csv('d:/code/py_machine/sp_data.csv')\n",
    "\n",
    "# 设置图表风格\n",
    "sns.set_style(\"whitegrid\")\n",
    "plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体显示中文\n",
    "plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题\n",
    "\n",
    "# 1. 各科成绩分布直方图\n",
    "plt.figure(figsize=(15, 5))\n",
    "plt.suptitle(\"各科成绩分布\")\n",
    "\n",
    "x_range = [-60, 60]\n",
    "plt.subplot(1, 3, 1)\n",
    "sns.histplot(df['math score'], kde=True, color='skyblue')\n",
    "plt.title('数学成绩分布')\n",
    "plt.xlim(x_range)\n",
    "\n",
    "plt.subplot(1, 3, 2)\n",
    "sns.histplot(df['reading score'], kde=True, color='salmon')\n",
    "plt.title('阅读成绩分布')\n",
    "plt.xlim(x_range)\n",
    "\n",
    "plt.subplot(1, 3, 3)\n",
    "sns.histplot(df['writing score'], kde=True, color='lightgreen')\n",
    "plt.title('写作成绩分布')\n",
    "plt.xlim(x_range)\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "# 2. 午餐与成绩关系\n",
    "plt.figure(figsize=(15, 5))\n",
    "plt.suptitle(\"午餐类型与成绩关系\")\n",
    "\n",
    "plt.subplot(1, 3, 1)\n",
    "sns.boxplot(x='lunch', y='math score', data=df)\n",
    "plt.title('数学成绩')\n",
    "plt.xticks([0, 1], ['空腹/少量', '标准'])\n",
    "\n",
    "plt.subplot(1, 3, 2)\n",
    "sns.boxplot(x='lunch', y='reading score', data=df)\n",
    "plt.title('阅读成绩')\n",
    "plt.xticks([0, 1], ['空腹/少量', '标准'])\n",
    "\n",
    "plt.subplot(1, 3, 3)\n",
    "sns.boxplot(x='lunch', y='writing score', data=df)\n",
    "plt.title('写作成绩')\n",
    "plt.xticks([0, 1], ['空腹/少量', '标准'])\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "# 3. 种族与平均成绩关系\n",
    "plt.figure(figsize=(10, 6))\n",
    "race_scores = df.groupby('race/ethnicity')[['math score', 'reading score', 'writing score']].mean()\n",
    "race_scores.plot(kind='bar', figsize=(10, 6))\n",
    "plt.title('不同种族学生的平均成绩')\n",
    "plt.ylabel('成绩')\n",
    "plt.xlabel('种族/民族')\n",
    "plt.xticks(rotation=45)\n",
    "plt.legend(['数学', '阅读', '写作'])\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "# 4. 性别与成绩关系\n",
    "plt.figure(figsize=(15, 5))\n",
    "plt.suptitle(\"性别与成绩关系\")\n",
    "\n",
    "plt.subplot(1, 3, 1)\n",
    "sns.boxplot(x='gender', y='math score', data=df)\n",
    "plt.title('数学成绩')\n",
    "\n",
    "plt.subplot(1, 3, 2)\n",
    "sns.boxplot(x='gender', y='reading score', data=df)\n",
    "plt.title('阅读成绩')\n",
    "\n",
    "plt.subplot(1, 3, 3)\n",
    "sns.boxplot(x='gender', y='writing score', data=df)\n",
    "plt.title('写作成绩')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.ensemble import RandomForestClassifier\n",
    "from sklearn.preprocessing import LabelEncoder\n",
    "from sklearn.metrics import accuracy_score\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# 设置中文字体显示\n",
    "plt.rcParams['font.sans-serif'] = ['SimHei']  # 使用黑体显示中文\n",
    "plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题\n",
    "\n",
    "# 定义成绩分段函数\n",
    "def grade_distribution(score):\n",
    "    if score < -20:\n",
    "        return 'E: < -20'\n",
    "    elif -20 <= score < -10:\n",
    "        return 'D: -20~-10'\n",
    "    elif -10 <= score < 0:\n",
    "        return 'C: -10~0'\n",
    "    elif 0 <= score < 10:\n",
    "        return 'B: 0~10'\n",
    "    else:\n",
    "        return 'A: >10'\n",
    "\n",
    "# 读取数据\n",
    "df = pd.read_csv('d:/code/py_machine/sp_data.csv')\n",
    "\n",
    "# 对成绩进行分段\n",
    "df['math_grade'] = df['math score'].apply(grade_distribution)\n",
    "df['reading_grade'] = df['reading score'].apply(grade_distribution)\n",
    "df['writing_grade'] = df['writing score'].apply(grade_distribution)  # 修正列名\n",
    "\n",
    "# 将parent_edu_score转为字符串类型（实际无该列不影响，若数据有需确保存在）\n",
    "df['parent_edu_score'] = df.get('parent_edu_score', pd.Series(dtype='float')).astype(str)  \n",
    "\n",
    "# 特征选择\n",
    "X = df[['race/ethnicity', 'lunch', 'test preparation course']]\n",
    "\n",
    "# 标签编码\n",
    "label_encoders = {}\n",
    "for col in X.columns:\n",
    "    le = LabelEncoder()\n",
    "    X[col] = le.fit_transform(X[col])\n",
    "    label_encoders[col] = le\n",
    "\n",
    "# 目标变量\n",
    "y_math = df['math_grade']\n",
    "y_reading = df['reading_grade']  \n",
    "y_writing = df['writing_grade']  \n",
    "\n",
    "# 拆分训练集和测试集 (80% 训练集, 20% 测试集)\n",
    "X_train, X_test, y_math_train, y_math_test = train_test_split(X, y_math, test_size=0.2, random_state=42)\n",
    "X_train, X_test, y_reading_train, y_reading_test = train_test_split(X, y_reading, test_size=0.2, random_state=42)  \n",
    "X_train, X_test, y_writing_train, y_writing_test = train_test_split(X, y_writing, test_size=0.2, random_state=42)  \n",
    "\n",
    "# 构建模型\n",
    "model_math = RandomForestClassifier(n_estimators=100, random_state=42)\n",
    "model_reading = RandomForestClassifier(n_estimators=100, random_state=42)\n",
    "model_writing = RandomForestClassifier(n_estimators=100, random_state=42)\n",
    "\n",
    "# 训练模型\n",
    "model_math.fit(X_train, y_math_train)\n",
    "model_reading.fit(X_train, y_reading_train)\n",
    "model_writing.fit(X_train, y_writing_train)\n",
    "\n",
    "# 测试模型准确率\n",
    "math_pred = model_math.predict(X_test)\n",
    "reading_pred = model_reading.predict(X_test)\n",
    "writing_pred = model_writing.predict(X_test)\n",
    "\n",
    "print(f'数学成绩预测准确率: {accuracy_score(y_math_test, math_pred):.2%}')\n",
    "print(f'阅读成绩预测准确率: {accuracy_score(y_reading_test, reading_pred):.2%}')\n",
    "print(f'写作成绩预测准确率: {accuracy_score(y_writing_test, writing_pred):.2%}')\n",
    "\n",
    "# 定义预测函数（含饼状图可视化）\n",
    "def predict_grades(race, lunch, test_preparation):\n",
    "    # 数据编码\n",
    "    race_encoded = label_encoders['race/ethnicity'].transform([race])[0] if race in label_encoders['race/ethnicity'].classes_ else np.nan\n",
    "    lunch_encoded = label_encoders['lunch'].transform([lunch])[0] if lunch in label_encoders['lunch'].classes_ else np.nan\n",
    "    test_encoded = label_encoders['test preparation course'].transform([test_preparation])[0] if test_preparation in label_encoders['test preparation course'].classes_ else np.nan\n",
    "\n",
    "    X_input = pd.DataFrame([[race_encoded, lunch_encoded, test_encoded]],\n",
    "                          columns=['race/ethnicity', 'lunch', 'test preparation course'])\n",
    "\n",
    "    # 预测概率\n",
    "    math_proba = model_math.predict_proba(X_input)[0] if not X_input.isnull().values.any() else [0] * len(model_math.classes_)\n",
    "    reading_proba = model_reading.predict_proba(X_input)[0] if not X_input.isnull().values.any() else [0] * len(model_reading.classes_)\n",
    "    writing_proba = model_writing.predict_proba(X_input)[0] if not X_input.isnull().values.any() else [0] * len(model_writing.classes_)\n",
    "\n",
    "    # 创建结果DataFrame，按分类顺序整理\n",
    "    result_df = pd.DataFrame({\n",
    "        'grades': model_math.classes_,  # 用模型分类结果保证顺序一致\n",
    "        'math_probability': math_proba,\n",
    "        'reading_probability': reading_proba,\n",
    "        'writing_probability': writing_proba\n",
    "    })\n",
    "    result_df['math_probability'] = result_df['math_probability'].round(4)  \n",
    "    result_df['reading_probability'] = result_df['reading_probability'].round(4)\n",
    "    result_df['writing_probability'] = result_df['writing_probability'].round(4)\n",
    "\n",
    "    # 显示结果表格\n",
    "    print(f\"\\n对于种族={race}, 午餐={lunch}, 课程准备={test_preparation} 的学生：\")\n",
    "    print(result_df.to_string(index=False))\n",
    "\n",
    "    # 绘制饼状图（数学、阅读、写作分别绘制）\n",
    "    fig, axes = plt.subplots(1, 3, figsize=(15, 5))\n",
    "    subjects = ['math', 'reading', 'writing']\n",
    "    prob_cols = ['math_probability', 'reading_probability', 'writing_probability']\n",
    "    for i in range(3):\n",
    "        axes[i].pie(result_df[prob_cols[i]], labels=result_df['grades'], autopct='%1.1f%%', startangle=90)\n",
    "        axes[i].set_title(f'{subjects[i]} 成绩等级概率分布')\n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "\n",
    "    return result_df\n",
    "\n",
    "# 示例预测\n",
    "predict_grades('group A', '1', '0')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
